#include "driver/gpio.h"
#include "driver/uart.h"
#include "mdf_common.h"
#include "mwifi.h"

/**
 * Signal Define
 **/
#define Command_UnLighten 3
#define Command_Lighten 2
#define Command_Activate 1
#define Command_Config 0

static int receive_socket = -1;
static int send_socket = -1;
static const char *TAG = "Wisight";
const char *ok = "ok";
static esp_netif_t *netif_sta = NULL;
uint8_t linked_addr[MWIFI_ADDR_LEN * 10] = {0x0};
uint8_t linked_num = 0;
uint8_t sta_mac[MWIFI_ADDR_LEN] = {0};
char sta_mac_str[18];
SemaphoreHandle_t link_handle;
SemaphoreHandle_t mwifi_handle;
SemaphoreHandle_t mwifi_root_handle;
bool light_control_flag = false;
bool signal_send_flag = false;

typedef struct socket_receive_send {
  int receive_socket;
  int send_socket;
} socket_receive_send;

/**
 * @brief Create a tcp client
 * return is a combine of a send socket and receive socket.
 * with regard to reliability, the communication is with a ok signal to info the sender.
 * the writer and read socket is divided into send_socket and receive_socket.
 */
static socket_receive_send
socket_tcp_client_create(const char *ip, uint16_t port, uint32_t *counter) {
  mdf_err_t ret = ESP_OK;
  int receive_sockfd = -1;
  int bind_sockfd = -1;
  socket_receive_send result;
  struct sockaddr_in server_addr = {
      .sin_family = AF_INET,
      .sin_port = htons(port),
      .sin_addr.s_addr = inet_addr(ip),
  };
  struct sockaddr_in server_addr_bind = {
      .sin_family = AF_INET,
      .sin_port = htons(port),
      .sin_addr.s_addr = htonl(INADDR_ANY),
  };
  struct sockaddr_in source_addr;
  uint addr_len = sizeof(source_addr);

  receive_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  MDF_ERROR_GOTO(receive_sockfd < 0, ERR_EXIT_RECEIVE, "Fail to Create Socket, sockfd: %d, Retry: %d", receive_sockfd, *counter);
  ret = connect(receive_sockfd, (struct sockaddr *)&server_addr,
                sizeof(struct sockaddr_in));
  MDF_ERROR_GOTO(
      ret < 0, ERR_EXIT_RECEIVE,
      "Fail to Connect Socket , Return: %d, ip: %s, port: %d, Retry: %d", ret,
      ip, port, *counter);

  bind_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  MDF_ERROR_GOTO(bind_sockfd < 0, ERR_EXIT_SEND, "Fail to Create Socket, sockfd: %d, Retry: %d", bind_sockfd, *counter);
  ret = bind(bind_sockfd, (struct sockaddr *)&server_addr_bind, sizeof(struct sockaddr_in));
  MDF_ERROR_GOTO(ret < 0, ERR_EXIT_SEND, "Fail to bind");
  ret = listen(bind_sockfd, 1);
  MDF_ERROR_GOTO(ret < 0, ERR_EXIT_SEND, "Fail to listen");
  int sock = accept(bind_sockfd, (struct sockaddr *)&source_addr, &addr_len);
  MDF_ERROR_GOTO(sock < 0, ERR_EXIT_ACCEPT, "Fail to accept With Error %d", sock);
  MDF_LOGI("Connect Server Successfully, IP: %s:%d", ip, port);
  *counter = 0;
  result.receive_socket = receive_sockfd;
  result.send_socket = sock;
  close(bind_sockfd);
  return result;

ERR_EXIT_RECEIVE:
  if (receive_sockfd != -1) {
    close(receive_sockfd);
  }
  ++*counter;
  result.receive_socket = -1;
  result.send_socket = -1;
  return result;
ERR_EXIT_SEND:
  if (bind_sockfd != -1) {
    close(receive_sockfd);
    close(bind_sockfd);
  }
  ++*counter;
  result.receive_socket = -1;
  result.send_socket = -1;
  return result;
ERR_EXIT_ACCEPT:
  if (bind_sockfd != -1) {
    close(receive_sockfd);
    close(bind_sockfd);
    close(sock);
  }
  ++*counter;
  result.receive_socket = -1;
  result.send_socket = -1;
  return result;
}

void TCP_receive_task(void *arg) {
  char *data = MDF_MALLOC(CONFIG_DATA_BUFFER_LENGTH);
  size_t size = CONFIG_DATA_BUFFER_LENGTH;
  size_t counter = 0;
  uint8_t dest_addr[MWIFI_ADDR_LEN] = {0x0};
  uint32_t mac_data[MWIFI_ADDR_LEN] = {0};
  mdf_err_t ret = MDF_OK;
  mwifi_data_type_t data_type = {0x0};
  data_type.group = false;
  cJSON *json_root = NULL;
  cJSON *json_addr = NULL;
  MDF_LOGI("TCP client read task is running");

  while (mwifi_is_connected()) {
    if (receive_socket == -1 || send_socket == -1) {
      if (receive_socket != -1) {
        close(receive_socket);
        receive_socket = -1;
      }
      if (send_socket != -1) {
        close(send_socket);
        send_socket = -1;
      }
      vTaskDelay(100 / portTICK_RATE_MS);
      socket_receive_send sock = socket_tcp_client_create(
          CONFIG_SERVER_IP, CONFIG_SERVER_PORT, &counter);
      receive_socket = sock.receive_socket;
      send_socket = sock.send_socket;

      if (receive_socket == -1 || send_socket == -1) {
        vTaskDelay(500 / portTICK_RATE_MS);
        continue;
      }
    }

    memset(data, 0, CONFIG_DATA_BUFFER_LENGTH);
    ret = read(receive_socket, data, size);
    if (ret <= 0) {
      MDF_LOGW("<%s> TCP read", strerror(errno));
      close(receive_socket);
      receive_socket = -1;
      continue;
    }
    ret = write(receive_socket, ok, 3);
    if (ret <= 0) {
      MDF_LOGW("<%s> TCP read write back", strerror(errno));
      close(receive_socket);
      receive_socket = -1;
      continue;
    }
    MDF_LOGD("TCP read, %d, size: %d", receive_socket, size);

    json_root = cJSON_Parse(data);
    MDF_ERROR_CONTINUE(!json_root, "cJSON_Parse, data format error");

    json_addr = cJSON_GetObjectItem(json_root, "dest_addr");
    if (json_addr) {
      sscanf(json_addr->valuestring, MACSTR, mac_data, mac_data + 1, mac_data + 2, mac_data + 3, mac_data + 4, mac_data + 5);
      for (int i = 0; i < MWIFI_ADDR_LEN; i++) dest_addr[i] = mac_data[i];
      ret = mwifi_write(dest_addr, &data_type, data, strlen(data) + 1, true);
      MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mwifi_root_write", mdf_err_to_name(ret));
    } else {
      json_addr= NULL;
      MDF_LOGW("Address not found");
    }
    cJSON_Delete(json_root);
  }

  MDF_LOGI("TCP client read task is exit");

  close(receive_socket);
  receive_socket = -1;
  MDF_FREE(data);
  vTaskDelete(NULL);
}

void TCP_send_task(void *arg) {
  mdf_err_t ret = MDF_OK;
  char *data = MDF_CALLOC(1, CONFIG_DATA_BUFFER_LENGTH);
  size_t size = CONFIG_DATA_BUFFER_LENGTH;
  uint8_t src_addr[MWIFI_ADDR_LEN] = {0x0};
  mwifi_data_type_t data_type = {0x0};


  while (mwifi_is_connected()) {
    MDF_LOGI("TCP client write task is running");
    size = CONFIG_DATA_BUFFER_LENGTH - 1;
    memset(data, 0, CONFIG_DATA_BUFFER_LENGTH);
    ret = mwifi_root_read(src_addr, &data_type, data, &size, portMAX_DELAY);
    if (receive_socket == -1 || send_socket == -1) {
      vTaskDelay(500 / portTICK_RATE_MS);
      MDF_LOGI("TCP not connected root send failes");
      continue;
    }
    MDF_LOGI("TCP send data %s", data);
    MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mwifi_root_read", mdf_err_to_name(ret));
    ret = write(send_socket, data, size);
    MDF_ERROR_CONTINUE(ret <= 0, "<%s> TCP write", strerror(errno));
    size = CONFIG_DATA_BUFFER_LENGTH;
    ret = read(send_socket, data, size);
    MDF_ERROR_CONTINUE((ret <= 0) || (data[0] != 'o') || (data[1] != 'k'), "<%s> TCP write", strerror(errno));
    MDF_LOGD("Send TCP Activate Signal by " MACSTR, MAC2STR(src_addr));
  }
  MDF_LOGI("TCP client write task is exit");

  close(send_socket);
  send_socket = -1;
  MDF_FREE(data);
  vTaskDelete(NULL);
}

static void node_read_task(void *arg) {
  mdf_err_t ret = MDF_OK;
  mwifi_data_type_t data_type = {0x0};
  uint8_t src_addr[MWIFI_ADDR_LEN] = {0x0};
  const size_t size = CONFIG_DATA_BUFFER_LENGTH;
  char *data = MDF_MALLOC(CONFIG_DATA_BUFFER_LENGTH);
  char *json_data;
  cJSON *json_root;
  cJSON *json_linkmacs = NULL;
  int json_command = 0;
  MDF_LOGI("Node read task is running");

  while (true) {
    if (!mwifi_is_connected()) {
      vTaskDelay(500 / portTICK_RATE_MS);
      continue;
    }
    memset(data, 0, CONFIG_DATA_BUFFER_LENGTH);
    ret = mwifi_read(src_addr, &data_type, data, &size, portMAX_DELAY);
    MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mwifi_read", mdf_err_to_name(ret));

    json_root = cJSON_Parse(data);
    json_data = cJSON_Print(json_root);
    MDF_LOGD("node received %s", json_data);
    free(json_data);
    json_command = cJSON_GetObjectItem(json_root, "command") -> valueint;
    if (json_command == Command_Config) {
      json_linkmacs = cJSON_GetObjectItem(json_root, "link_mac");
      ret = xSemaphoreTake(link_handle, portMAX_DELAY);
      MDF_ERROR_CONTINUE(ret != pdTRUE, "Failed to Get Mutex When Get Config Information");
      int i = cJSON_GetArraySize(json_linkmacs);
      uint32_t macs[MWIFI_ADDR_LEN * 10];
      for(int cnt = 0;cnt < i;cnt++){
          cJSON *client_list  = cJSON_GetArrayItem(json_linkmacs, cnt);
          if(client_list == NULL) {
            continue;
          }
          sscanf(cJSON_GetObjectItem(client_list , "mac")->valuestring, MACSTR, macs + 6 * cnt, macs + 6 * cnt + 1, macs + 6 * cnt + 2, macs + 6 * cnt + 3, macs + 6 * cnt + 4, macs + 6 * cnt + 5);
      }
      cJSON_Delete(json_root);
      for (int j = 0; j < i * MWIFI_ADDR_LEN; ++j) {
        linked_addr[j] = macs[j];
      }
      for (int j = i * MWIFI_ADDR_LEN; j < 10 * MWIFI_ADDR_LEN; ++j) {
        linked_addr[j] = 0;
      }
      ret = xSemaphoreGive(link_handle);
      linked_num = i;
      MDF_LOGD("Config Node Get %d MACs", linked_num);
    }
    else if (json_command == Command_Activate) {
      light_control_flag = true;
    }
  }
  MDF_FREE(data);
  cJSON_Delete(json_root);
  MDF_LOGW("Note read task is exit");
  vTaskDelete(NULL);
}

static void node_write_task(void *arg) {
  mwifi_data_type_t data_type_nonroot = {0};
  mwifi_data_type_t data_type_root = {0};
  char *data;
  char *Activate;
  int ret = MDF_OK;
  size_t size = MWIFI_ADDR_LEN;
  cJSON *root;

  root = cJSON_CreateObject();
  cJSON_AddStringToObject(root, "mac", sta_mac_str);
  cJSON_AddNumberToObject(root, "command", Command_Activate);
  data = cJSON_PrintUnformatted(root);
  size = strlen(data) + 1;
  Activate = MDF_MALLOC(size);
  strcpy(Activate, data);
  free(data);
  cJSON_Delete(root);
  MDF_LOGI("NODE task is running");
  while (true) {
    if (!mwifi_is_connected()) {
      vTaskDelay(500 / portTICK_RATE_MS);
      continue;
    }
    if (signal_send_flag) {
      MDF_LOGW("Dected");
      ret = xSemaphoreTake(mwifi_root_handle, portMAX_DELAY);
      MDF_ERROR_CONTINUE(ret != pdTRUE, "Failed to Get mwifi_root Mutex");
      ret = mwifi_write(NULL, &data_type_root, Activate, size, true);
      MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mwifi_write_to_root", mdf_err_to_name(ret));
      xSemaphoreGive(mwifi_root_handle);
      light_control_flag = true;
      ret = xSemaphoreTake(link_handle, portMAX_DELAY);
      MDF_ERROR_CONTINUE(ret != pdTRUE, "Failed to Get Mutex When Sending Message In Mesh Communication");
      if (linked_num) {
        ret = 0;
        for (uint8_t i = 0;i<linked_num;++i){
          ret = mwifi_write(linked_addr+MWIFI_ADDR_LEN*i, &data_type_nonroot, Activate, size, true) + ret;
          MDF_LOGI("Node Write to %d %d %d %d %d %d, %d, %d", linked_addr[MWIFI_ADDR_LEN*i],
          linked_addr[MWIFI_ADDR_LEN*i + 1], linked_addr[MWIFI_ADDR_LEN*i + 2], linked_addr[MWIFI_ADDR_LEN*i + 3],
          linked_addr[MWIFI_ADDR_LEN*i + 4], linked_addr[MWIFI_ADDR_LEN*i + 5], ret, linked_num);
        }
        MDF_ERROR_GOTO(ret != MDF_OK, FREE_MEM_UNLOCK, "<%s> mwifi_write_to_leaf", mdf_err_to_name(ret));
      }
      xSemaphoreGive(link_handle);

      MDF_LOGI("Node Send Successful, size: %d", size);
      signal_send_flag = false;
      vTaskDelay(100 / portTICK_RATE_MS);
      continue;

    FREE_MEM_UNLOCK:
      xSemaphoreGive(link_handle);
      continue;

    } else {
      vTaskDelay(100 / portTICK_RATE_MS);
    }
  }

  MDF_FREE(data);
  MDF_LOGW("NODE task is exit");

  vTaskDelete(NULL);
}

static void led_control_task(void *arg) {
  char *data;
  char *Lighten;
  char *UnLighten;
  int ret;
  size_t lighten_size;
  size_t unlighten_size;
  mwifi_data_type_t data_type_root = {0};
  cJSON *root;

  root = cJSON_CreateObject();
  cJSON_AddStringToObject(root, "mac", sta_mac_str);
  cJSON_AddNumberToObject(root, "command", Command_Lighten);
  data = cJSON_PrintUnformatted(root);
  lighten_size = strlen(data) + 1;
  Lighten = MDF_MALLOC(lighten_size);
  strcpy(Lighten, data);
  free(data);
  cJSON_Delete(root);

  root = cJSON_CreateObject();
  cJSON_AddStringToObject(root, "mac", sta_mac_str);
  cJSON_AddNumberToObject(root, "command", Command_UnLighten);
  data = cJSON_PrintUnformatted(root);
  unlighten_size = strlen(data) + 1;
  UnLighten = MDF_MALLOC(unlighten_size);
  strcpy(UnLighten, data);
  free(data);
  cJSON_Delete(root);
  vTaskDelay(1000 * 10 / portTICK_RATE_MS);
  while (true) {
    if (light_control_flag) {
      gpio_set_level(CONFIG_LIGHT_GPIO, 1);
      vTaskDelay(100 / portTICK_RATE_MS);
      ret = xSemaphoreTake(mwifi_root_handle, portMAX_DELAY);
      MDF_ERROR_CONTINUE(ret != pdTRUE, "Failed to Get mwifi_root Mutex");
      ret = mwifi_write(NULL, &data_type_root, Lighten, lighten_size, true);
      MDF_LOGW("Lightened");
      xSemaphoreGive(mwifi_root_handle);

      light_control_flag = false;
      vTaskDelay(1000 / portTICK_RATE_MS);
      if (gpio_get_level(CONFIG_SENSOR_GPIO)){
          light_control_flag = true;
          signal_send_flag = true;
      }
      vTaskDelay(1000 / portTICK_RATE_MS);
      if (gpio_get_level(CONFIG_SENSOR_GPIO)){
          light_control_flag = true;
          signal_send_flag = true;
      }
      vTaskDelay(1000 / portTICK_RATE_MS);
      if (gpio_get_level(CONFIG_SENSOR_GPIO)){
          light_control_flag = true;
          signal_send_flag = true;
      }
      vTaskDelay(1000 / portTICK_RATE_MS);
      if (gpio_get_level(CONFIG_SENSOR_GPIO)){
          light_control_flag = true;
          signal_send_flag = true;
      }
      vTaskDelay(1000 / portTICK_RATE_MS);
      if (gpio_get_level(CONFIG_SENSOR_GPIO)){
          light_control_flag = true;
          signal_send_flag = true;
      }
      ret = xSemaphoreTake(mwifi_root_handle, portMAX_DELAY);
      MDF_ERROR_CONTINUE(ret != pdTRUE, "Failed to Get mwifi_root Mutex");
      ret = mwifi_write(NULL, &data_type_root, UnLighten, unlighten_size, true);
      xSemaphoreGive(mwifi_root_handle);

      gpio_set_level(CONFIG_LIGHT_GPIO, 0);
    } else {
      vTaskDelay(100 / portTICK_RATE_MS);
    }
    continue;
  }

  MDF_FREE(Lighten);
  MDF_FREE(UnLighten);
  MDF_LOGW("LED task is exit");
  vTaskDelete(NULL);
}

/**
 * @brief Timed printing system information
 */
static void print_system_info_timercb(void *timer) {
  uint8_t primary = 0;
  wifi_second_chan_t second = 0;
  mesh_addr_t parent_bssid = {0};
  mesh_assoc_t mesh_assoc = {0x0};
  wifi_sta_list_t wifi_sta_list = {0x0};

  esp_wifi_ap_get_sta_list(&wifi_sta_list);
  esp_wifi_get_channel(&primary, &second);
  esp_wifi_vnd_mesh_get(&mesh_assoc);
  esp_mesh_get_parent_bssid(&parent_bssid);

  MDF_LOGI("System information, channel: %d, layer: %d, self mac: %s, parent bssid: " MACSTR
           ", parent rssi: %d, node num: %d, free heap: %u",
           primary, esp_mesh_get_layer(), sta_mac_str,
           MAC2STR(parent_bssid.addr), mesh_assoc.rssi,
           esp_mesh_get_total_node_num(), esp_get_free_heap_size());

  for (int i = 0; i < wifi_sta_list.num; i++) {
    MDF_LOGI("Child mac: " MACSTR, MAC2STR(wifi_sta_list.sta[i].mac));
  }

#ifdef MEMORY_DEBUG

  if (!heap_caps_check_integrity_all(true)) {
    MDF_LOGE("At least one heap is corrupt");
  }

  mdf_mem_print_heap();
  mdf_mem_print_record();
  mdf_mem_print_task();
#endif /**< MEMORY_DEBUG */
}

static mdf_err_t wifi_init() {
  mdf_err_t ret = nvs_flash_init();
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

  if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
      ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    MDF_ERROR_ASSERT(nvs_flash_erase());
    ret = nvs_flash_init();
  }

  MDF_ERROR_ASSERT(ret);

  MDF_ERROR_ASSERT(esp_netif_init());
  MDF_ERROR_ASSERT(esp_event_loop_create_default());
  ESP_ERROR_CHECK(esp_netif_create_default_wifi_mesh_netifs(&netif_sta, NULL));
  MDF_ERROR_ASSERT(esp_wifi_init(&cfg));
  MDF_ERROR_ASSERT(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
  MDF_ERROR_ASSERT(esp_wifi_set_mode(WIFI_MODE_STA));
  MDF_ERROR_ASSERT(esp_wifi_set_ps(WIFI_PS_NONE));
  MDF_ERROR_ASSERT(esp_mesh_set_6m_rate(false));
  MDF_ERROR_ASSERT(esp_wifi_start());
  esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac);
  sprintf(sta_mac_str, MACSTR, sta_mac[0], sta_mac[1], sta_mac[2], sta_mac[3], sta_mac[4], sta_mac[5]);
  return MDF_OK;
}

/**
 * @brief All module events will be sent to this task in esp-mdf
 *
 * @Note:
 *     1. Do not block or lengthy operations in the callback function.
 *     2. Do not consume a lot of memory in the callback function.
 *        The task memory of the callback function is only 4KB.
 */
static mdf_err_t event_loop_cb(mdf_event_loop_t event, void *ctx) {
  MDF_LOGI("event_loop_cb, event: %d", event);

  switch (event) {
  case MDF_EVENT_MWIFI_STARTED:
    MDF_LOGI("MESH is started");
    break;

  case MDF_EVENT_MWIFI_PARENT_CONNECTED:
    MDF_LOGI("Parent is connected on station interface");

    if (esp_mesh_is_root()) {
      esp_netif_dhcpc_start(netif_sta);
    }

    break;

  case MDF_EVENT_MWIFI_PARENT_DISCONNECTED:
    MDF_LOGI("Parent is disconnected on station interface");
    break;

  case MDF_EVENT_MWIFI_ROUTING_TABLE_ADD:
  case MDF_EVENT_MWIFI_ROUTING_TABLE_REMOVE:
    MDF_LOGI("total_num: %d", esp_mesh_get_total_node_num());
    break;

  case MDF_EVENT_MWIFI_ROOT_GOT_IP: {
    MDF_LOGI("Root obtains the IP address. It is posted by LwIP stack " "automatically");
    xTaskCreate(TCP_send_task, "TCP_write_task", 4 * 1024, NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, NULL);
    xTaskCreate(TCP_receive_task, "TCP_read_task", 4 * 1024, NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, NULL);
    break;
  }

  default:
    break;
  }

  return MDF_OK;
}

static void IRAM_ATTR gpio_isr_handler(void *arg) {
  uint32_t gpio_num = (uint32_t)arg;
  if (gpio_num == CONFIG_SENSOR_GPIO) {
    signal_send_flag = true;
  }
}

static void light_sensor_init() {
  gpio_config_t io_conf;
  // disable interrupt
  io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
  // set as output mode
  io_conf.mode = GPIO_MODE_OUTPUT;
  // bit mask of the pins that you want to set,e.g.LED
  io_conf.pin_bit_mask = 1 << CONFIG_LIGHT_GPIO;
  // disable pull-down mode
  io_conf.pull_down_en = 1;
  // disable pull-up mode
  io_conf.pull_up_en = 0;
  // configure GPIO with the given settings
  gpio_config(&io_conf);

  // interrupt of rising edge
  io_conf.intr_type = GPIO_PIN_INTR_POSEDGE;
  // bit mask of the pins
  io_conf.pin_bit_mask = 1 << CONFIG_SENSOR_GPIO;
  // set as input mode
  io_conf.mode = GPIO_MODE_INPUT;
  gpio_config(&io_conf);
  // start gpio task
  xTaskCreate(led_control_task, "led_control_task", 2048, NULL, 10, NULL);

  // install gpio isr service
  gpio_install_isr_service(0);
  // hook isr handler for specific gpio pin
  gpio_isr_handler_add(CONFIG_SENSOR_GPIO, gpio_isr_handler,
                       (void *)CONFIG_SENSOR_GPIO);
}

void app_main() {
  mwifi_init_config_t cfg = MWIFI_INIT_CONFIG_DEFAULT();
  mwifi_config_t config = {
      .router_ssid = CONFIG_ROUTER_SSID,
      .router_password = CONFIG_ROUTER_PASSWORD,
      .mesh_id = CONFIG_MESH_ID,
      .mesh_password = CONFIG_MESH_PASSWORD,
  };
  link_handle = xSemaphoreCreateMutex();
  mwifi_handle = xSemaphoreCreateMutex();
  mwifi_root_handle = xSemaphoreCreateMutex();
  /**
   * @brief Set the log level for serial port printing.
   */
  esp_log_level_set("*", ESP_LOG_INFO);
  esp_log_level_set(TAG, ESP_LOG_DEBUG);

  /**
   * @brief Initialize wifi mesh.
   */
  MDF_ERROR_ASSERT(mdf_event_loop_init(event_loop_cb));
  MDF_ERROR_ASSERT(wifi_init());
  MDF_ERROR_ASSERT(mwifi_init(&cfg));
  MDF_ERROR_ASSERT(mwifi_set_config(&config));
  MDF_ERROR_ASSERT(mwifi_start());
  light_sensor_init();


  /**
   * @breif Create handler
   */
  xTaskCreate(node_write_task, "node_write_task", 4 * 1024, NULL,
              CONFIG_MDF_TASK_DEFAULT_PRIOTY, NULL);
  xTaskCreate(node_read_task, "node_read_task", 4 * 1024, NULL,
              CONFIG_MDF_TASK_DEFAULT_PRIOTY, NULL);

  TimerHandle_t timer = xTimerCreate("print_system_info", 10000 / portTICK_RATE_MS, true, NULL, print_system_info_timercb);
  xTimerStart(timer, 0);
}
